package com.future.service.Impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.future.allUtils.utils.CacheClient;
import com.future.allUtils.utils.SecurityUtils;
import com.future.domain.DTO.PageDTO;
import com.future.domain.DTO.PsychologicalKnowledgeDTO;
import com.future.domain.PsychologicalKnowledge;
import com.future.domain.Result;
import com.future.domain.query.PsyKnowledgeQuery;
import com.future.domain.vo.PsyKnowledgeVO;
import com.future.exception.BusinessException;
import com.future.mapper.PsychologicalKnowledgeMapper;
import com.future.service.IPsychologicalKnowledgeService;

import lombok.extern.slf4j.Slf4j;


import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;


import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.future.allUtils.constants.CodeConstants.PROJECT_VIEWS_UPDATE_ERROR;
import static com.future.allUtils.constants.RedisCacheConstants.*;


@Service
@Slf4j
public class IPsychologicalKnowledgeServiceImpl extends ServiceImpl<PsychologicalKnowledgeMapper, PsychologicalKnowledge> implements IPsychologicalKnowledgeService {

     @Resource
     private StringRedisTemplate stringRedisTemplate;
     @Resource
     private CacheClient cacheClient;


     private final String PSYCHOLOGICAL_KNOWLEDGE_LOCK_PREFIX = "psycho:knowledge:lock:";

    @Override
    public Result saveKnowledge(PsychologicalKnowledgeDTO knowledgeDTO) {
        //1.获取当前用户id
        Long currentUserId = SecurityUtils.getCurrentUserId();
        //2.参数校验
        String checkParam = checkParam(knowledgeDTO);
        //1.参数校验
        if(!"success".equals(checkParam)){
            return Result.fail(checkParam);
        }


        //2.对文章内容进行base64编码
        String encodeUrlSafe = Base64.encodeUrlSafe(knowledgeDTO.getContent());

        PsychologicalKnowledge knowledge = new PsychologicalKnowledge();
        List<String> tag = knowledgeDTO.getTag();
         StringBuffer sb = new StringBuffer();
         for(int i=0;i<tag.size();i++){
             sb.append(tag.get(i));
             if(i != tag.size()-1){
                 sb.append(",");
             }
         }
         log.info(sb.toString());


        //3.文章入数据库
        boolean save = save(knowledge
                .setAuthorId(currentUserId)
                .setTitle(knowledgeDTO.getTitle())
                .setKnowledgeType(knowledgeDTO.getKnowledgeType())
                .setContent(encodeUrlSafe)
                .setTag(sb.toString())
                .setViews(0)
                .setSource(knowledgeDTO.getSource())
        );
        log.info("knowledge :{}",knowledge);
        //4.redis缓存浏览量
        if(save){
            try{
                stringRedisTemplate.opsForZSet().add(PSYCHOLOGICAL_KNOWLEDGE_VIEWS_PREFIX, JSONUtil.toJsonStr(knowledge),knowledge.getId());
                stringRedisTemplate.opsForZSet().add(PSYCHOLOGICAL_KNOWLEDGE_VIEWS_PREFIX, String.valueOf(knowledge.getId()),0);
                stringRedisTemplate.expire(PSYCHOLOGICAL_KNOWLEDGE_VIEWS_PREFIX, 7L, TimeUnit.DAYS);
            }catch (RuntimeException e){
                throw new RuntimeException("redis缓存失败");
            }
            return Result.ok("发布成功");
        }else {
            return Result.fail("发布失败");
        }
    }


    @Override
    public Result getKnowledge(Long id) {
        //如果登录，获取登录的id
        Long currentUserId =0L;
        try {
            currentUserId = SecurityUtils.getCurrentUserId();
        } catch (RuntimeException e) {
             log.info("用户未登录");
        }
        PsyKnowledgeVO knowledge = cacheClient.queryWithLogicalExpire(PSYCHOLOGICAL_KNOWLEDGE_PREFIX , id, PsyKnowledgeVO.class, ID->this.getBaseMapper().selectKnowledge(id), 7L, TimeUnit.DAYS);
        int flag=0;
        if(ObjectUtil.isNull(knowledge)) {
            flag =1;
            knowledge = baseMapper.selectKnowledge(id);
            if (ObjectUtil.isNotNull(knowledge)) {
                //缓存入数据库
                cacheClient.setWithLogicalExpire(PSYCHOLOGICAL_KNOWLEDGE_PREFIX + knowledge.getId(), knowledge, 7L, TimeUnit.DAYS);
            }
            if(ObjectUtil.isNull(knowledge)){
                return Result.fail("心理知识不存在");
            }
        }
        //2.如果不是从数据库查询的，从redis中查浏览量
        if(flag != 1){
            Double score = stringRedisTemplate.opsForZSet().score(PSYCHOLOGICAL_KNOWLEDGE_VIEWS_PREFIX, String.valueOf(knowledge.getId()));
            log.info("score :{}",score);
            if (score != null) {
                knowledge.setViews(score.intValue());
            }else {
                //从数据库中查
                knowledge = baseMapper.selectKnowledge(id);
                log.info("从数据库中查询浏览量");
                stringRedisTemplate.opsForZSet().add(PSYCHOLOGICAL_KNOWLEDGE_VIEWS_PREFIX, String.valueOf(knowledge.getId()),knowledge.getViews());
            }
        }

        //UV逻辑 : 统一用户一天内多次访问只算做一次
        //1.判断用户是否访问过
       //开启单独的线程去跟新数据库
        if(currentUserId != 0L){
            Boolean visit = stringRedisTemplate.opsForValue().setIfAbsent(PSYCHOLOGICAL_KNOWLEDGE_VIEWS_PREFIX +knowledge.getId()+":"+currentUserId,"true",1L,TimeUnit.DAYS);
            log.info("views :{}",knowledge.getViews());
            //24小时内没有访问过 ： 更新数据库
            if(BooleanUtil.isTrue(visit)) {
                //更新数据库
                ApplicationContext applicationContext = SpringUtil.getApplicationContext();
                IPsychologicalKnowledgeService proxy = applicationContext.getBean(IPsychologicalKnowledgeService.class);
                proxy.updateViews(id, knowledge.getViews());
            }
        }
        log.info("knowledge :{}",knowledge);
        log.info("主线程结束了");
        return Result.ok(knowledge.setContent(Base64.decodeStr(knowledge.getContent())));
    }
    /**
     *  异步更新浏览量
     *  @param id 文章id
     *  @param oldViews 旧的浏览量
     */
    @Async
    public void updateViews(Long id,int oldViews){
        log.info("异步更新浏览量");
        //key
        String key = PSYCHOLOGICAL_KNOWLEDGE_LOCK_PREFIX+id;
        //1.获取锁
        int maxRetries = 3;
        int retryCount = 0;

        try {
            while(retryCount < maxRetries) {
                if (tryLock(key)) {
                    break;
                }
                retryCount++;
                try {
                    Thread.sleep(100); // 等待一段时间后重试
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
            if(retryCount >= maxRetries) {
                throw new RuntimeException("更新浏览量失败");
            }
            //2.更新数据库
            LambdaUpdateWrapper<PsychologicalKnowledge> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(PsychologicalKnowledge::getId,id).set(PsychologicalKnowledge::getViews,oldViews+1);
            update(updateWrapper);
            stringRedisTemplate.opsForZSet().incrementScore(PSYCHOLOGICAL_KNOWLEDGE_VIEWS_PREFIX, String.valueOf(id),1);
        } catch (RuntimeException e) {
            throw new BusinessException(PROJECT_VIEWS_UPDATE_ERROR,"浏览量更新失败");
        } finally {
            //3.释放锁
            unlock(key);
        }
    }


    /**
     *  尝试获取锁
     *  @param key 锁的key  , 锁存在时间 ： 10 秒
     *  @return 是否获取成功
     */
    private boolean tryLock(String key){
        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(PSYCHOLOGICAL_KNOWLEDGE_LOCK_PREFIX+key, "1", 10, TimeUnit.SECONDS);
        return BooleanUtil.isTrue(flag);
    }
    /**
     *  释放锁
     *  @param key 锁的key
     */
    private void unlock(String key){
        stringRedisTemplate.delete(PSYCHOLOGICAL_KNOWLEDGE_LOCK_PREFIX+key);
    }

    @Override
    public Result removeKnowledge(Long id) {
        boolean remove = removeById(id);
        if(remove){
            stringRedisTemplate.delete(PSYCHOLOGICAL_KNOWLEDGE_PREFIX+id);
            stringRedisTemplate.opsForZSet().remove(PSYCHOLOGICAL_KNOWLEDGE_VIEWS_PREFIX, String.valueOf(id));
            return Result.ok("删除成功");
        }
        return Result.fail("删除失败");
    }

    @Override
    public Result updateKnowledge(PsychologicalKnowledgeDTO knowledge, Long id) {
        //1.检查参数
        String checkParam = checkParam(knowledge);
        if(!checkParam.equals("success")){
            return Result.fail(checkParam);
        }
        //2.类型转换
        PsychologicalKnowledge psychologicalKnowledge = BeanUtil.copyProperties(knowledge, PsychologicalKnowledge.class);
        //3.更新
        psychologicalKnowledge.setId(id);
        boolean update = updateById(psychologicalKnowledge);
        if (update) {
            //4.删除缓存
            stringRedisTemplate.delete(PSYCHOLOGICAL_KNOWLEDGE_PREFIX + id);
            return Result.ok("更新成功");
        }
        return Result.fail("更新失败");
    }
    static final String[] KNOWLEDGE_COLUMN = {"created_at","updated_at","views"};

    @Override
    public Result listByPage(PsyKnowledgeQuery query) {
        String type  = query.getKnowledgeType();
        String title = query.getTitle();
        String content = query.getContent();
        Long authorId = query.getAuthorId();
        String sortBy = query.getSortBy();
        if (sortBy != null && !sortBy.isEmpty()) {
            if (Arrays.asList(KNOWLEDGE_COLUMN).contains(sortBy)) {
                query.setSortBy(sortBy);
            } else {
                return Result.fail("排序字段错误");
            }
        }


        Page<PsychologicalKnowledge> page = query.toMpPage();

        LambdaQueryWrapper<PsychologicalKnowledge> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(type!= null && !type.isEmpty(),PsychologicalKnowledge::getKnowledgeType,type)
                .like(title!= null && !title.isEmpty(),PsychologicalKnowledge::getTitle,title)
                .like(content!= null && !content.isEmpty(),PsychologicalKnowledge::getContent,content)
                .eq(authorId!= null  ,PsychologicalKnowledge::getAuthorId,authorId)
        ;

        // 2.查询
        page(page,wrapper);
        // 3.数据非空校验
        List<PsychologicalKnowledge> records = page.getRecords();
        if (records.size() <= 0) {
            // 无数据，返回空结果
            return  Result.ok("没有更多的数据");
        }
        List<PsychologicalKnowledge> collect = records.stream().peek(knowledge -> knowledge.setContent(Base64.decodeStr(knowledge.getContent()))).collect(Collectors.toList());
        page.setRecords(collect);
        log.info("kowledge :{}", records);
        // 4.有数据，转换
        return Result.ok(PageDTO.pageConvert(page));
    }
    /**
     *  随机获取一个心理知识
     *  @param type 类型
     *  @return 心理知识
     */
    @Override
    public Result PsyKnowledge(String type){
        if(type == null || type.isEmpty()){
            return Result.fail("类型不能为空");
        }
        LambdaQueryWrapper<PsychologicalKnowledge> wrapper = new LambdaQueryWrapper<>();
        wrapper.like(PsychologicalKnowledge::getKnowledgeType,type);
        // 查询符合条件的所有心理知识
        List<PsychologicalKnowledge> knowledgeList = list(wrapper);
        log.info(knowledgeList.toString());
        // 检查列表是否为空
        if (knowledgeList.isEmpty()) {
            return Result.ok(null);
        }
        // 生成随机索引
        int randomIndex = new Random().nextInt(knowledgeList.size());
        log.info(randomIndex+"");
        PsychologicalKnowledge randomKnowledge = knowledgeList.get(randomIndex);

        return Result.ok(randomKnowledge.setContent(Base64.decodeStr(randomKnowledge.getContent())));
    }
    private String checkParam(PsychologicalKnowledgeDTO knowledgeDTO){
        if(knowledgeDTO.getKnowledgeType()== null|| knowledgeDTO.getKnowledgeType().isEmpty() ){
             return "类型不能为空";
        }
        if(knowledgeDTO.getTitle()== null|| knowledgeDTO.getTitle().isEmpty()){
            return "标题不能为空";
        }
        if(knowledgeDTO.getContent()== null|| knowledgeDTO.getContent().isEmpty()){
            return "内容不能为空";
        }
        if(knowledgeDTO.getTag()== null || knowledgeDTO.getTag().isEmpty()){
            return "标签不能为空";
        }
        if(knowledgeDTO.getSource()== null || knowledgeDTO.getSource().isEmpty()){
            return "来源不能为空";
        }
        return "success";
    }

    public  Result getMyKnowledge(Integer pageNum,Integer pageSize,String title){
           //1.获取当前用户
           Long currentUserId = SecurityUtils.getCurrentUserId();
           //2.查询
           Page<PsychologicalKnowledge> page = new Page<>(pageNum, pageSize);
           LambdaQueryWrapper<PsychologicalKnowledge> wrapper = new LambdaQueryWrapper<>();
           wrapper.eq(PsychologicalKnowledge::getAuthorId,currentUserId).like(title !=null &&!title.isEmpty()  ,PsychologicalKnowledge::getTitle,title);
           //3.分页查询
           page(page,wrapper);
           List<PsychologicalKnowledge> records = page.getRecords();
           records.forEach(knowledge -> knowledge.setContent(Base64.decodeStr(knowledge.getContent())));
           //4.返回
           return Result.ok(PageDTO.pageConvert(page));
    }
}
